package file;

import command.*;
import common.ParseException;
import common.Strings;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class File {

    private String fullFileName;
    private String fileName;
    private String fileAlias;
    private String fileAfter;
    private FileString fileFirstLine;
    private FileType fileType;
    private boolean fileStringsParsed;
    private ArrayList<FileString> strings;
    private ArrayList<Cmd> cmds;

    public File(java.io.File file) throws ParseException, IOException {
        this.fullFileName = file.getAbsolutePath();
        this.fileName = file.getName();
        this.fileAlias = this.fileName.toUpperCase().replaceAll("\\W", "_");
        this.fileAfter = "";
        this.fileType = FileType.PARSE;
        this.fileStringsParsed = false;
        this.strings = new ArrayList<FileString>();
        this.cmds = new ArrayList<Cmd>();

        BufferedReader reader;
        reader = new BufferedReader(new FileReader(fullFileName));
        String line;
        this.fileFirstLine = FileString.getFileString(this, 1, reader.readLine()).get(0);
        if (this.fileFirstLine.getOriginalString() != null) {
            while ((line = reader.readLine()) != null) {
                ArrayList<FileString> fileStrings = FileString.getFileString(this, strings.size() + 2, line);
                for (FileString tFileString : fileStrings)
                    strings.add(tFileString);
            }
        } else
            this.fileFirstLine = FileString.getFileString(this, 1, "").get(0);

        String[] spletted = Strings.trinAndDeleteDoubleSpace(this.fileFirstLine.getOriginalString())
                .split("[\\s=]{1,}");
        if (spletted.length == 1 && spletted[0].trim().equals(""))
            spletted = new String[0];
        if (spletted.length % 2 == 1)
            throw new ParseException(fileFirstLine,
                    "В первой строке файла должно быть четное число элементов разделенных пробелами и(или) знаком равно (KEY=VALUE KEY=VALUE ...)");
        if (spletted.length > 6)
            throw new ParseException(fileFirstLine, "Слишком много параметров в первой строке файла");
        for (int i = 0; i < spletted.length; i += 2) {
            if ("alias".equalsIgnoreCase(spletted[i]))
                this.fileAlias = spletted[i + 1].toUpperCase();
            else if ("after".equalsIgnoreCase(spletted[i]))
                this.fileAfter = spletted[i + 1].toUpperCase().replaceAll("\\W", "_");
            else if ("parse".equalsIgnoreCase(spletted[i])) {
                this.fileType = null;
                for (FileType tFileType : FileType.values())
                    if (tFileType.toString().equalsIgnoreCase(spletted[i + 1]))
                        this.fileType = tFileType;
                if (this.fileType == null)
                    throw new ParseException(fileFirstLine, "Неизвестное значение ключа PARSE: " + spletted[i + 1]);
            } else
                throw new ParseException(fileFirstLine, "Нераспознанный ключ: " + spletted[i]);
        }
    }

    public String getFullFileName() {
        return fullFileName;
    }

    public String getFileName() {
        return fileName;
    }

    public String getFileAlias() {
        return fileAlias;
    }

    public String getFileAfter() {
        return fileAfter;
    }

    public boolean isAfterAll() {
        return fileAfter.length() == 0;
    }

    public FileString getFileFirstLine() {
        return fileFirstLine;
    }

    public FileType getFileType() {
        return fileType;
    }

    public boolean isFileStringsParsed() {
        return fileStringsParsed;
    }

    public ArrayList<FileString> getStrings() {
        return strings;
    }

    public ArrayList<Cmd> getCmds() throws ParseException {
        if (fileType == FileType.BAD)
            throw new ParseException(fileFirstLine, "Попытка обратиться к командам BAD-файла");
        return cmds;
    }

    public void parse() throws ParseException {
        if (fileType == FileType.PARSE || fileType == FileType.INCLUDE) {
            ArrayList<Cmd> emptyCmds = new ArrayList<Cmd>();

            for (FileString fileString : strings) {
                Cmd cmd = Cmd.getCommand(fileString);
                if (cmd instanceof Empty || cmd instanceof Comment) {
                    if (emptyCmds.size() == 0 || emptyCmds.get(emptyCmds.size() - 1) instanceof Comment)
                        emptyCmds.add(cmd);
                    else if (cmd instanceof Comment)
                        emptyCmds.add(emptyCmds.size() - 1, cmd);
                } else {
                    if (cmds.size() == 0 || cmd.getParentFileString().getSpaceShift() <= cmds.get(cmds.size() - 1)
                            .getParentFileString().getSpaceShift()) {
                        for (Cmd tEmptyCmd : emptyCmds)
                            cmds.add(tEmptyCmd);
                        emptyCmds.clear();
                        cmds.add(cmd);
                    } else {
                        for (Cmd tEmptyCmd : emptyCmds)
                            cmds.get(cmds.size() - 1).addCmdToBody(cmd.getParentFileString().getSpaceShift(), tEmptyCmd);
                        emptyCmds.clear();
                        cmds.get(cmds.size() - 1).addCmdToBody(cmd);
                    }
                }
            }
            for (Cmd tEmptyCmd : emptyCmds)
                cmds.add(tEmptyCmd);
            emptyCmds.clear();

            fileStringsParsed = true;

            replaceAllInputs(cmds);
        } // if (fileType == FileType.STATIC_PARSE || fileType == FileType.STATIC_INCLUDE)
        else if (fileType == FileType.STATIC_PARSE || fileType == FileType.STATIC_INCLUDE) {
            for (FileString fileString : strings)
                cmds.add(new StaticText(FileString.getFileString(fileString.getParent(), fileString.getLineNum(), "~~{" + fileString.getOriginalString() + "}").get(0)));
            fileStringsParsed = true;
        } // if(fileType == FileType.STATIC_PARSE || fileType == FileType.STATIC_INCLUDE){
    }

    private void replaceAllInputs(ArrayList<Cmd> inpCmds) throws ParseException {
        for (int i = 0; i < inpCmds.size(); i++) {
            Cmd tCmd = inpCmds.get(i);
            if (tCmd instanceof Include) {
                if (tCmd.getBody().size() > 0)
                    throw new ParseException(tCmd.getParentFileString(), "@include не может иметь вложенное тело");
                inpCmds.remove(i);
                File inclFile = Files.getFile(((Include) tCmd).getAliasName());
                Files.addIncludedFile(tCmd.getParentFileString(), inclFile);

                if (!inclFile.isFileStringsParsed())
                    inclFile.parse();
                ArrayList<Cmd> inclCmds = inclFile.getCmds();
                for (int j = 0; j < inclCmds.size(); j++)
                    inpCmds.add(i + j, inclCmds.get(j).getCopy());
                i--;

                Files.removeIncludeFile(inclFile);
            } else if (tCmd instanceof Comment || tCmd instanceof Empty || tCmd instanceof StaticText || tCmd instanceof Var || tCmd instanceof Command || tCmd instanceof DynamicText) {
                if(tCmd.getBody().size()>0)
                    throw new ParseException(tCmd.getParentFileString(),"Данный тип элемента не может содержать вложенное тело: "+(tCmd.getClass()));
            } else if (tCmd.getBody().size() > 0) {
                replaceAllInputs(tCmd.getBody());
            }

        }
    }
}
